﻿using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Runtime.InteropServices;
using System.Text;
using System.Windows.Forms;

namespace Serviio.WHS
{
    class User32Helper
    {
        [DllImport("user32.dll")]
        static extern int SetParent(
            IntPtr hwndChild,
            IntPtr hwndParent);

        [DllImport("user32.dll")]
        static extern bool SetWindowPos(
            IntPtr hWnd,
            IntPtr hWndInsertAfter,
            int x,
            int y,
            int width,
            int height,
            SetWindowPosFlags flags);

        static readonly IntPtr HWND_TOPMOST = new IntPtr(-1);
        static readonly IntPtr HWND_NOTOPMOST = new IntPtr(-2);
        static readonly IntPtr HWND_TOP = new IntPtr(0);
        static readonly IntPtr HWND_BOTTOM = new IntPtr(1);

        [Flags()]
        private enum SetWindowPosFlags : uint
        {
            /// <summary>If the calling thread and the thread that owns the window are attached to different input queues, 
            /// the system posts the request to the thread that owns the window. This prevents the calling thread from 
            /// blocking its execution while other threads process the request.</summary>
            /// <remarks>SWP_ASYNCWINDOWPOS</remarks>
            SynchronousWindowPosition = 0x4000,
            /// <summary>Prevents generation of the WM_SYNCPAINT message.</summary>
            /// <remarks>SWP_DEFERERASE</remarks>
            DeferErase = 0x2000,
            /// <summary>Draws a frame (defined in the window's class description) around the window.</summary>
            /// <remarks>SWP_DRAWFRAME</remarks>
            DrawFrame = 0x0020,
            /// <summary>Applies new frame styles set using the SetWindowLong function. Sends a WM_NCCALCSIZE message to 
            /// the window, even if the window's size is not being changed. If this flag is not specified, WM_NCCALCSIZE 
            /// is sent only when the window's size is being changed.</summary>
            /// <remarks>SWP_FRAMECHANGED</remarks>
            FrameChanged = 0x0020,
            /// <summary>Hides the window.</summary>
            /// <remarks>SWP_HIDEWINDOW</remarks>
            HideWindow = 0x0080,
            /// <summary>Does not activate the window. If this flag is not set, the window is activated and moved to the 
            /// top of either the topmost or non-topmost group (depending on the setting of the hWndInsertAfter 
            /// parameter).</summary>
            /// <remarks>SWP_NOACTIVATE</remarks>
            DoNotActivate = 0x0010,
            /// <summary>Discards the entire contents of the client area. If this flag is not specified, the valid 
            /// contents of the client area are saved and copied back into the client area after the window is sized or 
            /// repositioned.</summary>
            /// <remarks>SWP_NOCOPYBITS</remarks>
            DoNotCopyBits = 0x0100,
            /// <summary>Retains the current position (ignores X and Y parameters).</summary>
            /// <remarks>SWP_NOMOVE</remarks>
            IgnoreMove = 0x0002,
            /// <summary>Does not change the owner window's position in the Z order.</summary>
            /// <remarks>SWP_NOOWNERZORDER</remarks>
            DoNotChangeOwnerZOrder = 0x0200,
            /// <summary>Does not redraw changes. If this flag is set, no repainting of any kind occurs. This applies to 
            /// the client area, the nonclient area (including the title bar and scroll bars), and any part of the parent 
            /// window uncovered as a result of the window being moved. When this flag is set, the application must 
            /// explicitly invalidate or redraw any parts of the window and parent window that need redrawing.</summary>
            /// <remarks>SWP_NOREDRAW</remarks>
            DoNotRedraw = 0x0008,
            /// <summary>Same as the SWP_NOOWNERZORDER flag.</summary>
            /// <remarks>SWP_NOREPOSITION</remarks>
            DoNotReposition = 0x0200,
            /// <summary>Prevents the window from receiving the WM_WINDOWPOSCHANGING message.</summary>
            /// <remarks>SWP_NOSENDCHANGING</remarks>
            DoNotSendChangingEvent = 0x0400,
            /// <summary>Retains the current size (ignores the cx and cy parameters).</summary>
            /// <remarks>SWP_NOSIZE</remarks>
            IgnoreResize = 0x0001,
            /// <summary>Retains the current Z order (ignores the hWndInsertAfter parameter).</summary>
            /// <remarks>SWP_NOZORDER</remarks>
            IgnoreZOrder = 0x0004,
            /// <summary>Displays the window.</summary>
            /// <remarks>SWP_SHOWWINDOW</remarks>
            ShowWindow = 0x0040,
        }

        [DllImport("user32.dll", SetLastError = true)]
        private static extern bool GetWindowInfo(
            IntPtr hWnd,
            ref WINDOWINFO WindowInfo);

        [StructLayout(LayoutKind.Sequential)]
        struct WINDOWINFO
        {
            public uint Size;
            public RECT WindowRect;
            public RECT ClientRect;
            public uint Style;
            public uint ExStyle;
            public uint WindowStatus;
            public uint WindowBordersX;
            public uint WindowBordersY;
            public ushort WindowType;
            public ushort CreatorVersion;

            public WINDOWINFO(Boolean? filler)
                : this()   // Allows automatic initialization of "cbSize" with "new WINDOWINFO(null/true/false)".
            {
                Size = (UInt32)(Marshal.SizeOf(typeof(WINDOWINFO)));
            }
        }

        [StructLayout(LayoutKind.Sequential)]
        public struct RECT
        {
            public int Left;
            public int Top;
            public int Right;
            public int Bottom;
        }

        public static Process StartChildProcess(Form form, string fileName, string arguments, bool fillClientArea, bool centerInClientArea, bool setAsChildProcess = true)
        {
            Process p = new Process();
            try
            {
                p.EnableRaisingEvents = true;
                p.StartInfo.FileName = fileName;
                p.StartInfo.Arguments = arguments;
                if (p.Start())
                {
                    if (!setAsChildProcess)
                        return p;

                    p.WaitForInputIdle(10000);
                    IntPtr pHwnd = p.MainWindowHandle;
                    if (pHwnd == IntPtr.Zero)
                    {
                        p.Kill();
                        return null;
                    }
                    IntPtr currentHwnd = Process.GetCurrentProcess().MainWindowHandle;
                    if (SetParent(pHwnd, currentHwnd) == 0)
                    {
                        if (Marshal.GetLastWin32Error() == 5) // access denied
                        {
                            // Need to launch privileged process that launches process on UAC enabled OS.
                            // Yeah, WHS isn't.
                        }
                        else
                        {
                            p.Kill();
                            return null;
                        }
                    }
                    if (fillClientArea)
                    {
                        SetWindowPos(pHwnd, HWND_TOP, 0, 0, form.ClientSize.Width, form.ClientSize.Height, 0);
                    }
                    if (centerInClientArea)
                    {
                        WINDOWINFO wInfo = new WINDOWINFO();
                        GetWindowInfo(p.MainWindowHandle, ref wInfo);
                        int width = wInfo.WindowRect.Right - wInfo.WindowRect.Left;
                        int height = wInfo.WindowRect.Bottom - wInfo.WindowRect.Top;
                        int x = (form.ClientSize.Width - width) / 2;
                        int y = (form.ClientSize.Height - height) / 2;
                        SetWindowPos(p.MainWindowHandle, HWND_TOP, x, y, 0, 0, SetWindowPosFlags.IgnoreResize);
                    }
                    return p;
                }
            }
            catch 
            {
                if (p != null)
                    p.Kill();
            }
            return null;
        }
    }
}
